#ifndef __C_PROC_WAIT_H__
#define __C_PROC_WAIT_H__

#include "std/list.h"
#include "std/debug.h"
#include "kernel/slab.h"
#include "kernel/spinlock.h"

#ifdef __cplusplus
extern "C" {
#endif

struct thread_struct;

typedef struct wait_queue
{
	listnode_t ownerlist;
	struct thread_struct* thd;
}
wait_queue_t;

typedef struct wait_queue_head
{
	spinlock_t spinlock;
	listnode_t head;
}
wait_queue_head_t;

#define WQ_FLAG_WAKEUP_INTERRUPTABLE		(1)
#define WQ_FLAG_WAKEUP_UNINTERRUPTABLE		(2)
#define WQ_FLAG_WAKEUP_PREEMPT				(4)
#define WQ_FLAG_WAKEUP_SYNCHRONOUS			(8)

#define WAIT_QUEUE_HEAD_INITIALIZER(name) \
	{ SPINLOCK_INITIALIZER(), LISTNODE_INITIALIZER((name).head) }

#define DECLARE_WAIT_QUEUE_HEAD(name) \
	wait_queue_head_t name = WAIT_QUEUE_HEAD_INITIALIZER(name)

#define WAIT_QUEUE_INITIALIZER(name) \
	{ LISTNODE_INITIALIZER((name).ownerlist), (thd) }

#define DECLARE_WAIT_QUEUE(name, thd) \
	wait_queue_t name = WAIT_QUEUE_INITIALIZER(name)

static inline void init_wait_queue(wait_queue_head_t* wq)
{
	kassert(NULL != wq);
	init_spinlock(&(wq->spinlock));
	listnode_init(wq->head);
}

static inline void add_wait_queue(wait_queue_head_t *wq, wait_queue_t* q)
{
	kassert(NULL != wq && NULL != q);
	spin_lock(&(wq->spinlock));
	listnode_add(wq->head, q->ownerlist);
	spin_unlock(&(wq->spinlock));
}

static inline bool remove_wait_queue(wait_queue_head_t* wq, wait_queue_t* q)
{
	kassert(NULL != wq && NULL != q);
	if (listnode_issingle(q->ownerlist))
		return false;

	spin_lock(&(wq->spinlock));
	listnode_del(q->ownerlist);
	spin_unlock(&(wq->spinlock));
	return true;
}

bool thread_wakeup(struct thread_struct* thd, uint flags);
static inline void wake_up(wait_queue_head_t* wq, uint flags)
{
	listnode_t *nxt;
	kassert(NULL != wq);

	spin_lock(&(wq->spinlock));
	nxt = wq->head.next;
	while (nxt != &(wq->head))
	{
		wait_queue_t *q = list_entry(wait_queue_t, ownerlist, nxt);
		nxt = nxt->next;
		spin_unlock(&(wq->spinlock));

		if (q->thd) thread_wakeup(q->thd, flags);
		spin_lock(&(wq->spinlock));
	}
	spin_unlock(&(wq->spinlock));
}

#ifdef __cplusplus
}
#endif
#endif
/* EOF */
